package cn.yangliu.nacos.oauth2.config;

import java.util.Objects;

import cn.yangliu.nacos.comm.tools.JsonUtils;
import cn.yangliu.nacos.comm.tools.ReflectUtils;
import cn.yangliu.nacos.oauth2.config.store.OAuthAuthentication;
import cn.yangliu.nacos.oauth2.config.store.OAuthRequest;
import cn.yangliu.nacos.oauth2.config.store.OAuthUsernamePasswordAuthenticationToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.store.redis.StandardStringSerializationStrategy;

/**
 * The interface String serialization strategy.
 *
 * @author 问道于盲
 * @date 2019 -04-19
 */
public class StringSerializationStrategy extends StandardStringSerializationStrategy {

    /**
     * Deserialize internal t.
     *
     * @param <T>   the type parameter
     * @param bytes the bytes
     * @param clazz the clazz
     * @return the t
     */
    @Override
    @SuppressWarnings("unchecked")
    @SneakyThrows
    protected <T> T deserializeInternal(byte[] bytes, Class<T> clazz) {
        ObjectMapper mapper = JsonUtils.getObjectMapper();

        if (!Objects.equals(OAuth2Authentication.class, clazz)) {
            return mapper.readValue(bytes, clazz);
        }

        OAuthAuthentication oAuthAuthentication = mapper.readValue(bytes, OAuthAuthentication.class);

        OAuthUsernamePasswordAuthenticationToken userAuthentication = oAuthAuthentication.getUserAuthentication();

        OAuthRequest oAuthRequest = oAuthAuthentication.getStoredRequest();

        OAuth2Request oAuth2Request = ReflectUtils.newInstance(OAuth2Request.class);
        if (Objects.nonNull(oAuthRequest)) {
            BeanUtils.copyProperties(oAuthRequest, oAuth2Request);
        }
        
        UsernamePasswordAuthenticationToken token = null;
        if (Objects.nonNull(userAuthentication)) {
            token = new UsernamePasswordAuthenticationToken(userAuthentication.getPrincipal(),
                    userAuthentication.getCredentials(), userAuthentication.getAuthorities());

        }
        OAuth2Authentication oAuth2Authentication =
                new OAuth2Authentication(oAuth2Request, token);

        return (T) oAuth2Authentication;

    }

    /**
     * Serialize internal byte [ ].
     *
     * @param object the object
     * @return the byte [ ]
     */
    @Override
    @SneakyThrows
    protected byte[] serializeInternal(Object object) {
        ObjectMapper mapper = JsonUtils.getObjectMapper();
        if (!(object instanceof OAuth2Authentication)) {
            return mapper.writeValueAsBytes(object);
        }

        OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) object;
        Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();

        OAuthAuthentication oAuthAuthentication = new OAuthAuthentication();
        BeanUtils.copyProperties(oAuth2Authentication, oAuthAuthentication);
        OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
        if (Objects.nonNull(oAuth2Request)) {
            OAuthRequest oAuthRequest = new OAuthRequest();
            BeanUtils.copyProperties(oAuth2Request, oAuthRequest);
            oAuthAuthentication.setStoredRequest(oAuthRequest);
        }

        oAuthAuthentication.setAuthorities(oAuth2Authentication.getAuthorities());
        if (Objects.nonNull(userAuthentication)) {
            OAuthUsernamePasswordAuthenticationToken token = new OAuthUsernamePasswordAuthenticationToken();
            BeanUtils.copyProperties(userAuthentication, token);
            oAuthAuthentication.setUserAuthentication(token);
        }

        return mapper.writeValueAsBytes(oAuthAuthentication);
    }
}
